package com.wuxuan.algorithm_homework.binarySearch;

/**
 * 二分查找
 */
public class BinarySearchDemo {

    /**
     * 二分查找
     *
     * @param nums   有序数组
     * @param target 要找的目标值
     * @return 返回target所在位置的索引
     */
    public int binarySearch(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int l = 0;
        int r = nums.length - 1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        return -1;
    }


    /**
     * 给你一个非负整数 x ，计算并返回 x 的算术平方根 。由于返回类型是整数，结果只保留 整数部分 ，小数部舍去。
     * 输入：x = 8
     * 输出：2
     * 解释：8 的算术平方根是 2.82842..., 由于返回类型是整数，小数部分将被舍去。
     *
     * @param x
     * @return
     */
    public int squareRoot(int x) {
        //题目是说非负数
        if (x < 0) {
            return -1;
        }
        //这里l从0开始，是考虑了0，0的算数平方根是0
        int l = 0;
        int r = x;
        int result = -1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (mid * mid == x) {
                return mid;
            } else if (mid * mid < x) {
                result = mid;
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        return result;

    }

    /**
     * 整数数组 nums 按升序排列，数组中的值 互不相同 。
     * 在传递给函数之前，nums 在预先未知的某个下标 k（0 <= k < nums.length）上进行了 旋转，使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]（下标 从 0 开始 计数）。例如， [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
     * 给你 旋转后 的数组 nums 和一个整数 target ，如果 nums 中存在这个目标值 target ，则返回它的下标，否则返回 -1 。
     * 你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
     *
     * @param nums
     * @param target
     * @return
     */
    public int findTargetByRotate(int[] nums, int target) {
//     示例 1：[4,5,6,7,0,1,2] , target = 0, return 4
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int l = 0;
        int r = nums.length - 1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            //先判断mid对应的值是在旋转区还是原区
            if (nums[mid] > nums[r]) {
                //旋转区
                //例：[4, 5, 6, 7, 0, 1, 2] (注：mid对应的值不一定是旋转区中max)
                //找target
                if (nums[mid] > target) {
                    //target可能在左边，可能在右边
                    //注意这里进一步要用同一个区域里面的边界left来与target比较！
                    if (nums[l] > target) {
                        //target只可能在右边了(右边可能有旋转区和原地区，无所谓)
                        l = mid + 1;
                    } else {
                        //target在nums[l]和nums[mid]之间
                        r = mid - 1;
                    }
                } else {
//                    nums[mid] < target
                    //target只可能在mid右边
                    l = mid + 1;
                }

            } else {
                //原区
                //例：[5, 6, 0, 1, 2, 3, 4]
                if (nums[mid] > target) {
                    //target肯定在mid左边
                    r = mid - 1;
                } else {
//                    nums[mid] < target
                    //target可能在mid左边，也可能在右边
                    //注意这里进一步要用同一个区域里面的边界right来与target比较！
                    if (nums[r] < target) {
                        //target只可能在左边
                        r = mid - 1;
                    } else {
                        //target只可能在nums[mid]和nums[r]之间
                        l = mid + 1;
                    }
                }
            }

        }
        return -1;

    }


    public static void main(String[] args) {
        //二分查找
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        BinarySearchDemo b = new BinarySearchDemo();
        int i = b.binarySearch(arr, 6);
        System.out.println("index = " + i);

        //平方根
        System.out.println(b.squareRoot(0));

        //旋转
        int[] arr2 = {6, 7, 8, 9, 10, 1, 2, 3, 4, 5};
        System.out.println(b.findTargetByRotate(arr2, 10));

    }
}
